/**************************************************************
*
* M3D_QUAT.H
*
* Quaternions support
**************************************************************/

enum m3dROT_ORDER {
   M3D_ROT_XYZ,
   M3D_ROT_YXZ,
};

class m3dQUAT {
public:
   float  s;       // scalar part of quaternion
   m3dV   v;       // vector part of quaternion

   void  Identity                (void);
   void  Invert                  (void);
   void  Invert                  (m3dQUAT *q) const { *q = *this; q->Invert(); }
   
   void  SetAngAxis              (float angle, m3dV const *axis);
   void  SetRotMatr              (const m3dMATR *rotMatr);
   void  SetMatr                 (const m3dMATR *pMatr);
   void  SetRot                  (const m3dV *norm1, const m3dV *norm2);
   BOOL  SetRot                  (const m3dV *norm1, const m3dV *norm2, float gain);
   BOOL  SetClampRotProp         (const m3dV *norm1, const m3dV *norm2, float speed, float maxStep, float minAngle = 0.f);
   BOOL  SetClampRot             (const m3dV *norm1, const m3dV *norm2, float maxAngle);
   BOOL  SetClampRotAxis         (const m3dV *norm1, const m3dV *norm2, const m3dV *axis, float maxAngle);
   BOOL  SetClampRotPropAxis     (const m3dV *norm1, const m3dV *norm2, const m3dV *axis, float speedCoef, float maxStep, float minAngle = 0.f);
   void  SetRotX                 (float angle);
   void  SetRotY                 (float angle);
   void  SetRotZ                 (float angle);
   void  SetRot                  (float x, float y, float z, m3dROT_ORDER order);
   BOOL  GetAngAxis              (float *angle, m3dV *axis) const;
   void  GetMatr                 (m3dMATR *pMatr) const;
   void  GetRotMatr              (m3dMATR *pMatr) const;
   void  GetEulerAngles          (m3dV *angles) const;
   BOOL  Check                   (void) const;
   float GetNorm                 (void) const
   {
      return m3dSqrt(m3dPow2(v.x) + m3dPow2(v.y) + m3dPow2(v.z) + m3dPow2(s));
   }
   void  Normalize      (void); 
   void  Clamp          (float gain);
   void  GetNormalized  (m3dQUAT *quat) const;
   void  Transform      (const m3dV *a, m3dV *res) const;
   void  Transform      (m3dMATR* matr, int operOrder) const;
   void  Transform      (const m3dQUAT* quat, m3dQUAT* res) const;
   void  Transform      (m3dQUAT* quat) const;

   __INLINE void  Transform      (m3dV *vct) const;

         m3dQUAT        (float angle, const m3dV *axis);
         m3dQUAT        (void): s(1.f), v(m3dVZero){}
};

/*****************************************************
* m3dQUAT::Transform (m3dV *vct)
*
*****************************************************/
__INLINE void m3dQUAT::Transform (m3dV * __RESTRICT vct) const
{
   m3dV tmp;
   Transform(vct, &tmp);
   *vct = tmp;
}

/*****************************************************
* m3dAddQuat ()
*
* Add quaternions
*****************************************************/
__INLINE void m3dAddQuat (const m3dQUAT * __RESTRICT a, const m3dQUAT * __RESTRICT b, m3dQUAT * __RESTRICT c)
{
   c->s = a->s + b->s;
   m3dAddVector(&a->v, &b->v, &c->v);
}
/*****************************************************
* m3dSubtractQuat()
*
* Subtract quaternions
*****************************************************/
__INLINE void m3dSubtractQuat (const m3dQUAT * __RESTRICT a, const m3dQUAT * __RESTRICT b, m3dQUAT * __RESTRICT c)
{
   c->s = a->s - b->s;
   m3dSubtractVector(&a->v, &b->v, &c->v);
}
/*****************************************************
* m3dScaleQuat ()
*
* Scale quaternion
*****************************************************/
__INLINE void m3dScaleQuat (const m3dQUAT * __RESTRICT a, float s, m3dQUAT * __RESTRICT c)
{
   c->s = a->s * s;
   m3dScaleVector(&a->v, s, &c->v);
}

/*****************************************************
* _m3dScaleQuat ()
*
* Scale quaternion
*****************************************************/
__INLINE void _m3dScaleQuat (m3dQUAT * __RESTRICT a, float s)
{
   a->s *= s;
   _m3dScaleVector(&a->v, s);
}

extern void    m3dMultiplyQuat      (const m3dQUAT *a, const m3dQUAT *b, float gain, m3dQUAT *c);
extern void    m3dMultiplyQuat      (const m3dQUAT *a, const m3dQUAT *b, m3dQUAT *c);
extern void    _m3dMultiplyQuat     (m3dQUAT *a, const m3dQUAT *b);
extern void    m3dDivideQuat        (const m3dQUAT *a, const m3dQUAT *b, m3dQUAT *c);
extern void    m3dClampQuat         (const m3dQUAT *src, const m3dQUAT *dest, float maxAngle, m3dQUAT *res);
extern void    _m3dClampQuat        (m3dQUAT *src, const m3dQUAT *dest, float maxAngle);

/*************************************************************
* m3dInnerProductQuat()
* 
*************************************************************/
__INLINE float m3dInnerProductQuat (const m3dQUAT * __RESTRICT a, const m3dQUAT * __RESTRICT b)
{
   return a->s * b->s + m3dDotProduct(&a->v, &b->v);
}

extern void    m3dLineCombine       (const m3dQUAT *a, const m3dQUAT *b, float kb, m3dQUAT *c);

extern void    m3dSlerpQuat         (const m3dQUAT *a, const m3dQUAT *b, float t, m3dQUAT *c);

#define        m3dIsEqualQuat(a, b) m3dIsEqualQuat_A(a, b, M3D_EPSILON)
#define        m3dIsZeroQuat(a)  m3dIsZeroQuat_A(a, M3D_EPSILON)
extern BOOL    m3dIsEqualQuat_A     (const m3dQUAT *a, const m3dQUAT *b, float acc);
extern BOOL    m3dIsZeroQuat_A      (const m3dQUAT *a, float acc);

//
// End-of-file M3D_QUAT.H
//
